﻿#include<iostream>
#include<thread>
#include<vector>
using namespace std;
#include<Windows.h>

wstring tetromino[7];
//设置地图大小
int nFieldWidth = 12;
int nFieldHeight = 18;
unsigned char* pField = nullptr;        //地图指针

//设置Console窗口大小,原点在左上角
int nScreenWidth = 80;
int nScreenHeight = 30;

/// <summary>
/// 返回基础方块在不同角度下，4x4矩阵的px，py所指向的具体编号（0-15）
/// </summary>
/// <param name="px">4x4矩阵的x</param>
/// <param name="py">4x4矩阵的y</param>
/// <param name="r">指代当前角度</param>
/// <returns>返回具体编号</returns>
int Rotate(int px, int py, int r)
{
	switch (r % 4)
	{
		case 0: return py * 4 + px;         //0度
		case 1: return 12 + py - px * 4;	//90度
		case 2: return 15 - py * 4 - px;	//180度
		case 3: return 3 - py + px * 4;		//270度
	}
	return 0;
}

/// <summary>
/// 判断该方块是否可放在当前区域
/// </summary>
/// <param name="nTetromino">方块编号</param>
/// <param name="nRotation">该方块的角度代号</param>
/// <param name="nPosX">地图的x</param>
/// <param name="nPosY">地图的y</param>
/// <returns>可放为true，否则为false</returns>
bool DoesPieceFit(int nTetromino, int nRotation, int nPosX, int nPosY) 
{
	// The field is full so zero spaces
	for (int px = 0; px < 4; px++)
		for (int py = 0; py < 4; py++) {
			int pi = Rotate(px, py, nRotation);

			int fi = (nPosY + py) * nFieldWidth + (nPosX + px);

			// Check that test is in bounds. Note out of bounds does
			// not necessarily mean a fail, as the long vertical piece
			// can have cells that lie outside the boundary, so we'll
			// just ignore them
			if (nPosX + px >= 0 && nPosX + px < nFieldWidth) {
				if (nPosY + py >= 0 && nPosY + py < nFieldHeight) {
					if (tetromino[nTetromino][pi] != L'.' && pField[fi] != 0)
						return false; // fail on first hit
				}
			}
		}

	return true;
}

int main()
{
	//设置七个基础方块形状，‘.’代表空白，‘X’代表块，4x4的矩阵块
	tetromino[0].append(L"..X...X...X...X.");
	tetromino[1].append(L"..X..XX..X......");
	tetromino[2].append(L".X...XX...X.....");
	tetromino[3].append(L".....XX..XX.....");
	tetromino[4].append(L"..X..XX...X.....");
	tetromino[5].append(L".....XX...X...X.");
	tetromino[6].append(L".....XX..X...X..");

	//初始化地图
	pField = new unsigned char[nFieldWidth * nFieldHeight];
	for (int x = 0; x < nFieldWidth; x++)
	{
		for (int y = 0; y < nFieldHeight; y++)
		{
			pField[y * nFieldWidth + x] =
				(x == 0 || x == nFieldWidth - 1 || y == nFieldHeight - 1) ? 9 : 0;
		}
	}

	//创建Console窗口，并初始化为‘ ’
	wchar_t* screen = new wchar_t[nScreenWidth * nScreenHeight];
	for (int i = 0; i < nScreenWidth * nScreenHeight; i++)
	{
		screen[i] = L' ';
	}
	HANDLE hConsole =
		CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
	SetConsoleActiveScreenBuffer(hConsole);
	DWORD dwBytesWritten = 0;


	bool bGameOver = false;                  //指代当前游戏是否结束

	//生产一个初始方块
	int nCurrentPiece = rand() % 7;          //当前方块类型
	int nCurrentRotation = 0;                //当前方块旋转状态
	int nCurrentX = nFieldWidth / 2;
	int nCurrentY = 0;

	bool bKey[4];							 //保存四个按键的状态（按下或抬起）
	bool bRotateHold = false;				 //指代是否持续按压旋转键

	int nSpeed = 20;                         //方块自动下落的速度，越小越快
	int nSpeedCounter = 0;					 //记录帧数
	bool bForceDown = false;                 //指代是否要进行一次自动下落

	int nPieceCount = 0;                     //记录已固定的方块数量
	int nScore = 0;							 //记录分数

	vector<int> vLines;                      //存储已成一行的y值

	wstring content = L" ABCDEFG=#";

	while (!bGameOver)
	{
		// GAME TIMING =====================
		this_thread::sleep_for(50ms);        //每帧间隔50ms
		nSpeedCounter++;
		bForceDown = (nSpeedCounter == nSpeed);

		// INPUT ===========================
		for (int k = 0; k < 4; k++)
		{														//右箭头 左箭头  下箭头  Z键
			bKey[k] = (0x8000 & GetAsyncKeyState((unsigned char)("\x27\x25\x28Z"[k]))) != 0;
		}

		// GAME LOGIC ======================
		//处理按键的移动
		nCurrentX += (bKey[0] && DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX + 1, nCurrentY)) ? 1 : 0;
		nCurrentX += (bKey[1] && DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX - 1, nCurrentY)) ? -1 : 0;
		nCurrentY += (bKey[2] && DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY + 1)) ? 1 : 0;
		if (bKey[3]) 
		{
			nCurrentRotation += (!bRotateHold && DoesPieceFit(nCurrentPiece, nCurrentRotation + 1, nCurrentX, nCurrentY)) ? 1 : 0;
			bRotateHold = true;
		}
		else
		{
			bRotateHold = false;
		}

		//处理自动下落
		if(bForceDown)
		{
			nSpeedCounter = 0;
			if (DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY + 1))
				nCurrentY++;
			else
			{
				//已到底，固定该方块，将当前方块数据写入地图信息中
				for (int px = 0; px < 4; px++)
				{
					for (int py = 0; py < 4; py++)
					{
						if (tetromino[nCurrentPiece][Rotate(px, py, nCurrentRotation)] == L'X')
							pField[(nCurrentY + py) * nFieldWidth + nCurrentX + px] = nCurrentPiece + 1;
					}
				}

				//依据固定方块数调整速度
				nPieceCount++;
				if(nPieceCount % 10 == 0)
				{
					if (nSpeed > 10) nSpeed--;
				}

				//判断是否成整行
				for (int py = 0; py < 4; py++)
				{
					if (nCurrentY + py < nFieldHeight - 1) 
					{
						bool bLine = true;
						for (int px = 1; px < nFieldWidth - 1; px++)
						{
							bLine &= pField[(nCurrentY + py) * nFieldWidth + px] != 0;
						}
						if (bLine) 
						{
							for (int px = 1; px < nFieldWidth-1; px++)
							{
								pField[(nCurrentY + py) * nFieldWidth + px] = 8;//整行，变为'='
							}
							vLines.push_back(nCurrentY + py);
						}
					}
				}

				//计算分数
				nScore += 25;
				if (!vLines.empty()) nScore += (1 << vLines.size()) * 100;

				//生成新方块
				nCurrentPiece = rand() % 7;
				nCurrentRotation = 0;
				nCurrentX = nFieldWidth / 2;
				nCurrentY = 0;

				//判断游戏是否结束
				bGameOver = !DoesPieceFit(nCurrentPiece, nCurrentRotation, nCurrentX, nCurrentY);
			}

		}


		// RENDER OUTPUT ==================

		// 绘制地图
		for (int x = 0; x < nFieldWidth; x++)
		{
			for (int y = 0; y < nFieldHeight; y++)
			{
				screen[(y + 2) * nScreenWidth + (x + 2)] = content[pField[y * nFieldWidth + x]];
			}
		}

		// 绘制当前方块
		for (int px = 0; px < 4; px++)
		{
			for (int py = 0; py < 4; py++)
			{
				if (tetromino[nCurrentPiece][Rotate(px, py, nCurrentRotation)] == L'X')
					screen[(nCurrentY + py + 2) * nScreenWidth + (nCurrentX + px + 2)] = nCurrentPiece + 'A';//65为A的ascii码
			}
		}

		// 绘制分数
		swprintf_s(&screen[2 * nScreenWidth + nFieldWidth + 6], 16, L"Score: %8d", nScore);

		//处理整行
		if(!vLines.empty())
		{
			WriteConsoleOutputCharacter(hConsole, screen, nScreenWidth * nScreenHeight, { 0,0 }, &dwBytesWritten);
			this_thread::sleep_for(400ms);//等待400ms后清除整行

			for(auto &v:vLines)
			{
				for(int px = 1;px<nFieldWidth-1;px++)
				{
					for (int py = v; py > 0; py--)
						pField[(nCurrentY + py) * nFieldWidth + px] = pField[(nCurrentY + py - 1) * nFieldWidth + px];
					pField[px] = 0;
				}
			}
			vLines.clear();
		}

		// 提交绘制Console窗口
		WriteConsoleOutputCharacter(hConsole, screen, nScreenWidth * nScreenHeight, {0,0}, &dwBytesWritten);
	}

	CloseHandle(hConsole);
	cout << "Game Over!! Score:" << nScore << endl;
	system("pause");
	return 0;
}